package org.infodavid.common.net;

import java.lang.ref.WeakReference;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * The Class NetUtil.
 */
public final class NetUtil {

	/** The network address. */
	private static InetAddress address = null;

	/** The HOSTNAME. */
	public static String HOSTNAME;

	/** The Constant log. */
	private static final Log LOGGER = LogFactory.getLog(NetUtil.class);

	/** The singleton. */
	private static WeakReference<NetUtil> singleton = null;

	static {
		HOSTNAME = getInstance().getHostName();
	}

	/**
	 * returns the singleton.
	 * @return <code>NetUtil</code>
	 */
	public static synchronized NetUtil getInstance() {
		if (singleton == null || singleton.get() == null) {
			singleton = new WeakReference<NetUtil>(new NetUtil());
		}

		return singleton.get();
	}

	/** The is debug enabled. */
	private final boolean isDebugEnabled = LOGGER.isDebugEnabled();

	/**
	 * Instantiates a new util.
	 */
	private NetUtil() {
		super();
	}

	/**
	 * Accept.
	 * @param inetAddress the inet address
	 * @return true, if successful
	 */
	private boolean accept(final InetAddress inetAddress) {
		return !(inetAddress.isLoopbackAddress() || inetAddress.isMulticastAddress() || inetAddress instanceof Inet6Address);
	}

	/**
	 * Gets the host address.
	 * @return the host address
	 */
	public String getHostAddress() {
		updateAddress();

		return address == null ? "Unknown" : address.getHostAddress();
	}

	/**
	 * Gets the host name.
	 * @return the host name
	 */
	public String getHostName() {
		updateAddress();

		return address == null ? "Unknown" : address.getCanonicalHostName();
	}

	/**
	 * Gets the host string.
	 * @return the host string
	 */
	public String getHostString() {
		return getHostName() + " (" + getHostAddress() + ')';
	}

	/**
	 * Update address.
	 */
	private synchronized void updateAddress() {
		if (address == null) {
			try {
				final Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();
				Enumeration<InetAddress> inetAddresses;
				InetAddress inetAddress = null;
				NetworkInterface netint;

				while (nets.hasMoreElements() && address == null) {
					netint = nets.nextElement();
					inetAddresses = netint.getInetAddresses();

					while (inetAddresses.hasMoreElements() && address == null) {
						inetAddress = inetAddresses.nextElement();

						if (isDebugEnabled) {
							LOGGER.debug("Found InetAddress: " + inetAddress.getHostAddress());
						}

						if (accept(inetAddress)) {
							address = inetAddress;
						}
					}
				}

				if (isDebugEnabled) {
					LOGGER.debug("Host known as " + inetAddress);
				}
			}
			catch (final Exception e) {
				if (isDebugEnabled) {
					LOGGER.debug("An error occurs", e);
				}
			}
		}
	}
}
